﻿using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Windows.Markup;
using System.Threading.Tasks;
using agree.mrs;

using alib;
using alib.Collections;
using alib.Debugging;
using alib.Enumerable;
using alib.Character;
using alib.Hashing;
using alib.String;

using agree.schema;

namespace agree.semi
{
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// 
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	[ContentProperty("Vpm")]
	public sealed class SemI : identity_base, ρρVpm
	{
		public SemI(IIdentity prv, Vpm vpm)
			: base(prv)
		{
			this.vpm = vpm;
		}
		public SemI(IIdentity prv)
			: this(prv, default(Vpm))
		{
		}

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		PredicateArgs predargs;
		public PredicateArgs PredicateArgs
		{
			get { return this.predargs; }
			set { this.predargs = value; }
		}

		Vpm vpm;
		public Vpm Vpm
		{
			get { return vpm; }
			set { vpm = value; }
		}
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// 
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	[DebuggerDisplay("preds={Count} unique-feats:{AllFeats.Count}")]
	public class PredicateArgs : IdentDict<String, AppropArgsList>, ISemantics
	{
		public PredicateArgs(IIdentity prv)
			: base(prv, 20000, StringComparer.InvariantCultureIgnoreCase)
		{
			(this.AllFeats = new StringIndex(StringComparison.OrdinalIgnoreCase)).Add("LBL");
		}

		public readonly StringIndex AllFeats;

		public Constant RegisterConstant(string value)
		{
			throw new NotImplementedException();
		}
		public Variable CreateInstance(System.Type t)
		{
			throw new NotImplementedException();
		}
		public SemType GetSemType<T>() where T : SemType
		{
			throw new NotImplementedException();
		}
	};


	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// 
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	[DebuggerDisplay("{ToString(),nq}")]
	public class AppropArgsList : IIdentList<AppropriateArgs>
	{
		public AppropArgsList()
		{
			arr = Collection<AppropriateArgs>.None;
		}

		[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
		AppropriateArgs[] arr;

		public void Add(AppropriateArgs aa)
		{
			alib.Array.arr.Append(ref arr, aa);
		}

		public bool ContainsFeature(String s_feat)
		{
			var _tmp = arr;
			for (int i = 0; i < _tmp.Length; i++)
				if (_tmp[i].ContainsFeature(s_feat))
					return true;
			return false;
		}

		public AppropriateArgs this[int index] { get { return arr[index]; } }
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public int Count { get { return arr.Length; } }
		public IEnumerator<AppropriateArgs> GetEnumerator() { return arr.Enumerator(); }
		IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
		public void CopyTo(Array array, int index) { arr.CopyTo(array, index); }
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		bool ICollection.IsSynchronized { get { return false; } }
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		Object ICollection.SyncRoot { get { return this; } }
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		IIdentity IIdentity.Trace { get { return null; } }

		public override String ToString()
		{
			return arr.StringJoin(Environment.NewLine);
		}
	};


	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// 
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	[DebuggerDisplay("{ToString(),nq}")]
	public class AppropriateArgs : IIdentList<ArgumentPosition>
	{
		public AppropriateArgs()
		{
		}

		[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
		ArgumentPosition[] arr;

		public void Add(ArgumentPosition arg_pos)
		{
			alib.Array.arr.Append(ref arr, arg_pos);
		}

		public bool ContainsFeature(String s_feat)
		{
			var _tmp = arr;
			for (int i = 0; i < _tmp.Length; i++)
				if (_tmp[i].feature.Equals(s_feat, StringComparison.OrdinalIgnoreCase))
					return true;
			return false;
		}

		public override String ToString()
		{
			StringBuilder sb = new StringBuilder();
			for (int i = 0; i < arr.Length; i++)
			{
				ArgumentPosition arg_pos = arr[i];
				if (i > 0)
					sb.Append(", ");

				var sap = arg_pos.ToString();
				if (arg_pos.optional)
					sap = "[ " + sap + " ]";

				sb.Append(sap);
			}

			return sb.ToString();
		}
		public ArgumentPosition this[int index] { get { return arr[index]; } }
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public int Count { get { return arr.Length; } }
		public IEnumerator<ArgumentPosition> GetEnumerator() { return arr.Enumerator(); }
		IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
		public void CopyTo(Array array, int index) { arr.CopyTo(array, index); }
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		bool ICollection.IsSynchronized { get { return false; } }
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		Object ICollection.SyncRoot { get { return this; } }
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		IIdentity IIdentity.Trace { get { return null; } }
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// 
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public struct ArgumentPosition
	{
		public ArgumentPosition(String s_feat, Constraint var_tmpl, bool optional)
		{
			this.feature = s_feat;
			this.template = var_tmpl;
			this.optional = optional;
		}

		public readonly String feature;

		public readonly Constraint template;

		public readonly bool optional;

		public override String ToString()
		{
			var sb = new StringBuilder();

			sb.Append(feature);
			sb.Append(" ");

			sb.Append(template.Type);
			if (template.Count > 0)
			{
				sb.Append(" {");
				int num = 0;
				foreach (var prop in template)
				{
					sb.AppendFormat(" {0} {1}", prop.Name, template[prop.Name]);
					if (++num != template.Count)
						sb.Append(",");
				}
				sb.Append(" }");
			}

			return sb.ToString();
		}
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// 
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public class PredicateArgsLoader : _monad<String>, ρρFilename
	{
		public PredicateArgsLoader(IRtParent parent, String filename)
			: base(parent, filename)
		{
		}

		public PredicateArgsLoader(IRtParent parent)
			: this(parent, default(String))
		{
		}


		///////////////////////////////////////////////////////////////////////
		/// 
		public String Filename
		{
			get { return base.t0; }
			set { base.t0 = value; }
		}
		/// 
		///////////////////////////////////////////////////////////////////////


		public override Object ProvideValue(IServiceProvider sp)
		{
			var fi = DirectoryPathSet.FindFileInfo(this, t0);
			if (fi == null)
				throw new FileNotFoundException("Sem-I file not found");

			this.output = new PredicateArgs(this);
			PredicateArgsLoad(File.ReadAllLines(fi.FullName, Encoding.UTF8));
			return output;
		}

		PredicateArgs output;
		String line;
		String[] toks;

		//this function assumes that substructure never goes more than one level deep
		void PredicateArgsLoad(String[] lines)
		{
			foreach (var _line in lines)
			{
				var split1 = _line.Split(Charset.colon, StringSplitOptions.RemoveEmptyEntries);
				if (split1.Length != 2)
					continue;

				this.line = _line;
				this.toks = split1[1].Replace(",", " , ").Replace(".", " .").Split(default(String[]), StringSplitOptions.RemoveEmptyEntries);

				var pred = split1[0].Trim().RemoveOuterQuotationMarks();
				AppropArgsList aal;
				if (!output.TryGetValue(pred, out aal))
					output.Add(pred, aal = new AppropArgsList());

				aal.Add(scan_args());
			}
		}

		AppropriateArgs scan_args()
		{
			var aa = new AppropriateArgs();

			int pos = 0;
			String next_tok;
			while (true)
			{
				bool optional = false;

				if (toks[pos] == "[")
				{
					optional = true;
					pos++;
				}

				String s_feat = GetCheckNextToken(pos++);
				String var_type = GetCheckNextToken(pos++);

				var var_tmpl = new Constraint(output, var_type);//output.RegisterConstant(var_type));

				if (pos < toks.Length && toks[pos] == "{")
				{
					++pos;
					while (true)
					{
						var prop_name = GetCheckNextToken(pos++);
						var prop_val = GetCheckNextToken(pos++);
						var_tmpl.Add(prop_name, prop_val);

						next_tok = toks[pos++];
						if (next_tok == "}")
							break;
						else if (next_tok != ",")
							throw new Exception(String.Format("expected ',' or '}' at {0}th token  but found {1}: {2}", pos - 1, toks[pos - 1], line));
					}
				}

				if (optional)
				{
					if (pos >= toks.Length || toks[pos] != "]")
						throw new Exception(String.Format("expected ']' at {0}th token but found {1}: {2}", pos, toks[pos], line));
					++pos;
				}

				output.AllFeats.Add(s_feat);
				aa.Add(new ArgumentPosition(s_feat, var_tmpl, optional));

				if (pos < toks.Length && toks[pos] == ".")
					break;

				if (pos >= toks.Length || toks[pos] != ",")
					throw new Exception(String.Format("expected ',' or '{{' or '.' at {0}th token but found {1}: {2}", pos, toks[pos], line));

				++pos;
			}

			if (pos < toks.Length - 1)
				throw new Exception(String.Format("unexpected '.' found before end of line: {0}", line));

			return aa;
		}

		String GetCheckNextToken(int pos)
		{
			if (pos >= toks.Length)
				throw new Exception(String.Format("Unexpected end of line: {0}", line));

			String next_tok = toks[pos];
			if (next_tok == "," || next_tok == "{" || next_tok == "}" || next_tok == ":" || next_tok == "[" || next_tok == "]")
				throw new Exception(String.Format("incorrect syntax for predicate arg description at tok_num {0}: {1}", pos, line));
			return next_tok;
		}
	};
}
